﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MIAP.Entidades;
using MIAP.Entidades.Enums;
using MIAP.Negocio;
using MIAP.Negocio.Autenticacion;

namespace MIAPWEB.Common
{

    /// <summary>    
    /// Clase Wrapper de las variables de sesión de la aplicación.
    /// </summary>
    public sealed class AppSessionManager
    {
        #region Constantes

        /// <summary>
        /// Instancia unica de la clase.
        /// </summary>
        public static readonly AppSessionManager Instance = new AppSessionManager();

        #endregion

        #region Constructores

        /// <summary>
        /// Constructor Privado. Evita la creacion de una instancia predeterminada de la clase <see cref="AppSessionManager"/>
        /// </summary>        
        private AppSessionManager()
        {
        }

        #endregion

        #region Propiedades Publicas

        /// <summary>
        /// Obtiene un valor que determina si el usuario de la aplicación se encuentra autenticado.
        /// </summary>
        public bool EstaAutenticado
        {
            get
            {
                return HttpContext.Current.Session["estaautenticado"] != null && (bool)HttpContext.Current.Session["estaautenticado"];

            }
            private set
            {
                HttpContext.Current.Session["estaautenticado"] = value;
            }
        }

        /// <summary>
        /// Obtiene el alias del usuario de la sesión.
        /// </summary>
        public string Usuario
        {
            get
            {
                return (string)HttpContext.Current.Session["usuario"];
            }

            private set
            {
                HttpContext.Current.Session["usuario"] = value;
            }
        }


        /// <summary>
        /// Obtiene el Id del usuario de la sesión.
        /// </summary>
        public int IdUsuario
        {
            get
            {
                return (int)HttpContext.Current.Session["IdUsuario"];
            }

            private set
            {
                HttpContext.Current.Session["IdUsuario"] = value;
            }
        }


        /// <summary>
        /// Obtiene el nombre completo del usuario de la sesión.
        /// </summary>
        public string NombreCompleto
        {
            get
            {
                return (string)HttpContext.Current.Session["nombreCompleto"];
            }

            private set
            {
                HttpContext.Current.Session["nombreCompleto"] = value;
            }
        }


        /// <summary>
        /// Obtiene la lista de permisos asociados al usuario de la sesión.
        /// </summary>
        public List<PermisoInfo> Permisos
        {
            get
            {
                return (List<PermisoInfo>)HttpContext.Current.Session["permisos"];
            }

            private set
            {
                HttpContext.Current.Session["permisos"] = value;
            }
        }

        /// <summary>
        /// Proyectos que estan asignados a cada pagina
        /// </summary>
        public Dictionary<string, IList<ProyectoInfo>> ProyectosPaginas
        {
            get
            {
                return (Dictionary<string, IList<ProyectoInfo>>)HttpContext.Current.Session["proyectosPaginas"];
            }
            private set
            {
                HttpContext.Current.Session["proyectosPaginas"] = value;
            }
        }

        #endregion

        #region Propiedades Privadas

        /// <summary>
        /// Obtiene el diccionario de relación entre URL's principales y URL's hijas.
        /// </summary>
        private IEnumerable<Tuple<string, string, string>> UrlsChildrenDictionary
        {
            get
            {
                if (HttpContext.Current.Session["urlschildrendictionary"] == null)
                {
                    HttpContext.Current.Session["urlschildrendictionary"] = AppUrls.GetUrlsRelationshipsDictionary();
                }

                return (List<Tuple<string, string, string>>)HttpContext.Current.Session["urlschildrendictionary"];
            }
        }

        #endregion

        #region Metodos

        /// <summary>
        /// Establece la información del usuario actual de la aplicación.
        /// </summary>
        /// <param name="usuario">objeto usuarui.</param>
        /// <param name="perfiles">Lista de perfiles asociados al usuario.</param>
        public void SetSessionUserInfo(UsuarioInfo usuario, IList<PerfilInfo> perfiles)
        {
            this.Usuario = usuario.AliasUsuario;

            this.IdUsuario = usuario.IdUsuario;

            this.NombreCompleto = usuario.NombreUsuario + " " + usuario.ApellidoPaterno + " " + usuario.ApellidoMaterno;

            this.SetPaginasPermisos(perfiles);

            this.EstaAutenticado = true;
        }

        /// <summary>
        /// 
        /// </summary>
        public void RecalculaPermisos()
        {
            var permisos = new AutenticacionNegocio().GePermissionsByUsuario(this.IdUsuario);

            this.SetPaginasPermisos(permisos);
        }

        /// <summary>
        /// Obtiene los proyectos activos a los cuales el usuario tiene permiso dependiendo de cada pagina
        /// </summary>
        /// <param name="urlPagina">pagina de la cual queremos obtener sus proyectos</param>
        /// <returns>Lista de objetos del tipo ProyectoInfo</returns>
        public List<ProyectoInfo> GetProyectosByPagina(string urlPagina)
        {
            var activo = Enum.GetName(typeof(Estatus), Estatus.Activo).Trim().ToUpper();
            return (from proyecto in this.ProyectosPaginas[urlPagina]
                    where proyecto.Estatus.ToUpper().Trim() == activo
                    select proyecto).ToList();
        }

        /// <summary>
        /// obtiene y establece los permisos asignados y las paginas con los proyectos a los cuales tiene acceso
        /// </summary>
        /// <param name="perfiles">Lista de perfiles de los cuales se obtendrá la información</param>
        private void SetPaginasPermisos(IList<PerfilInfo> perfiles)
        {
            var loProyectos = new Dictionary<string, IList<ProyectoInfo>>();
            var loPermisos = new List<PermisoInfo>();

            //si no es el "Super usuario" buscamos a que paginas tiene acceso y a que proyectos.
            if (perfiles.Any(elemento => elemento.Proyecto.IDProyecto == 0 && elemento.IdPerfil == 1))
            {
                var saPerfil = perfiles.First(elemento => elemento.Proyecto.IDProyecto == 0 && elemento.IdPerfil == 1);
                var saProyectos = new AdministrarProyectosNegocio().GetProyectosByFilters(null, true);
                foreach (var permisoInfo in saPerfil.Permisos)
                {
                    loPermisos.Add(permisoInfo);
                    loProyectos.Add(permisoInfo.Url, saProyectos);
                }
            }
            else
            {
                foreach (var perfilInfo in perfiles)
                {
                    var perfilProyecto = perfilInfo.Proyecto;
                    foreach (var permisoInfo in perfilInfo.Permisos)
                    {
                        if (!loProyectos.ContainsKey(permisoInfo.Url))
                        {
                            loPermisos.Add(permisoInfo);
                            loProyectos.Add(permisoInfo.Url, new List<ProyectoInfo> { perfilProyecto });
                        }
                        else if (!loProyectos[permisoInfo.Url].Contains(perfilProyecto))
                        {
                            loProyectos[permisoInfo.Url].Add(perfilProyecto);

                        }
                    }
                }
            }
            this.ProyectosPaginas = loProyectos;
            this.Permisos = loPermisos;
            if (loPermisos == null)
            {
                var stringe = string.Empty;
            }
        }

        /// <summary>
        /// Determina si el usuario autenticado cuenta con los permisos requeridos para acceder a la URI especificada.
        /// </summary>
        /// <param name="uriReferrer">URI que refiere.</param>
        /// <param name="uriRequested">URI que sera comprobado.</param>
        /// <returns>true si el usuario tiene permiso de acceso a la Uri especificada.</returns>
        public bool HasSessionUserAccessPermission(Uri uriReferrer, Uri uriRequested)
        {
            // Si no exite una url que refiera a la página a comprobar, entonces negar el acceso.
            if (uriReferrer == null)
            {
                return false;
            }

            string urlReferrer = string.Format("~{0}", uriReferrer.AbsolutePath);
            string urlRequested = string.Format("~{0}", uriRequested.AbsolutePath);

            if (this.EstaAutenticado && urlRequested == AppUrls.Default)
            {
                return true;
            }

            // Comprobar si la URL pertenece a una URL 
            if (this.UrlsChildrenDictionary.Any(f => f.Item2 == urlReferrer && f.Item3 == urlRequested))
            {
                // Si la URL es una URL hija entonces reemplazar la url requerida por la url mediante la cual se comprobara el acceso.
                urlRequested = this.UrlsChildrenDictionary.Single(f => f.Item2 == urlReferrer && f.Item3 == urlRequested).Item1;
            }

            // Ejecutar la comprobación de permisos de acceso.
            return this.Permisos != null && this.Permisos.Any(f => f.Url == urlRequested);
        }

        /// <summary>
        /// Determina si el usuario autenticado cuenta con los permisos requeridos para acceder a la URL especificada.
        /// </summary>
        /// <param name="url">URL que sera comprobada.</param>
        /// <returns>true si el usuario tiene permiso de acceso a la URL especificada.</returns>
        public bool HasSessionUserAccessPermission(string url)
        {
            if (this.EstaAutenticado && url == AppUrls.Default)
            {
                return true;
            }

            var resultado = this.Permisos.Any(f => f.Url == url);
            return resultado;
        }

        #endregion

    }
}